package com.zl.learning.lambada;

/**
 * Created by zhaolei on 2017/6/23.
 */

import com.zl.learning.lambada.model.Student;
import org.junit.Test;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamTest {

    private List<Student> list = new ArrayList<>();

    {
        Student student = new Student();
        /*主键*/
        student.setId(2);
        /*姓名*/
        student.setName("张三");
        /*班级*/
        student.setClasses(1);
        /*年级*/
        student.setGrade(1);
        /*成绩*/
        student.setScore(80);
        list.add(student);

        Student student1 = new Student();
        student1.setId(1);
        student1.setName("李四");
        student1.setClasses(1);
        student1.setGrade(1);
        student1.setScore(60);
        list.add(student1);

        Student student2 = new Student();
        student2.setId(3);
        student2.setName("王二麻子");
        student2.setClasses(2);
        student2.setGrade(1);
        student2.setScore(90);
        list.add(student2);

        Student student3 = new Student();
        student3.setId(4);
        student3.setName("王五");
        student3.setClasses(2);
        student3.setGrade(1);
        student3.setScore(59.5);
        list.add(student3);

        Student student4 = new Student();
        student4.setId(8);
        student4.setName("小明");
        student4.setClasses(1);
        student4.setGrade(2);
        student4.setScore(79.5);
        list.add(student4);

        Student student5 = new Student();
        student5.setId(5);
        student5.setName("小红");
        student5.setClasses(2);
        student5.setGrade(2);
        student5.setScore(99);
        list.add(student5);

        Student student6 = new Student();
        student6.setId(7);
        student6.setName("小黑");
        student6.setClasses(2);
        student6.setGrade(2);
        student6.setScore(45);
        list.add(student6);

        Student student7 = new Student();
        student7.setId(6);
        student7.setName("小白");
        student7.setClasses(1);
        student7.setGrade(2);
        student7.setScore(88.8);
        list.add(student7);

        Student student8 = new Student();
        student8.setId(6);
        student8.setName("小白");
        student8.setClasses(1);
        student8.setGrade(2);
        student8.setScore(88.8);
        list.add(student8);
    }

    @Test
    public void test1() {
        List<Student> l2 = new ArrayList<>(list.stream().collect(Collectors.toMap(Student::getGrade, c -> c, (e1, e2) -> e2)).values());
        System.out.println(list);
        Map<String, List<String>> map2 = list.stream().collect(Collectors.groupingBy(Student::getName, Collectors.mapping(r -> String.valueOf(r.getScore()), Collectors.toList())));
        Map<String, String> map = list.stream().collect(Collectors.toMap(Student::getName, r -> String.valueOf(r.getScore()), (c1, c2) -> c1 + "-" + c2));
        System.out.println(map);
    }

    @Test
    public void test2() {
        /*去重，去除重复对象（每个属性的值都一样的），需要注意的是要先重写对象TestStreamModel的equals和hashCode方法*/
        System.out.println("去重前：" + list);
        List<Student> distinctList = list.stream().distinct().collect(Collectors.toList());
        System.out.println("去重后：" + distinctList);
    }

    @Test
    public void test3() {
        /*排序，按id升续排列，如果要降续则改成：(a, b) -> b.getId() - a.getId(); a和b都是变量名（可以按自己意愿取名字），都是list中的对象的实例*/
        System.out.println("排序前：" + list);
//        List<Student> sortList = list.stream().sorted((a, b) -> a.getId() - b.getId()).collect(Collectors.toList());
        List<Student> sortList = list.stream().sorted(Comparator.comparingInt(Student::getId)).collect(Collectors.toList());
        System.out.println("排序后" + sortList);
    }

    @Test
    public void test5() {
        /*过滤，按照自己的需求来筛选list中的数据，比如我筛选出不及格的（小于60分）的人,t为实例*/
        System.out.println("过滤后：" + list);
        Predicate<Student> predicate = (t) -> t.getScore() < 50;
//        List<Student> filterList = list.stream().filter(t -> t.getScore() < 60).collect(Collectors.toList());
        List<Student> filterList = list.stream().filter(predicate).collect(Collectors.toList());
        System.out.println("过滤后" + filterList);
    }

    @Test
    public void test6() {
        /*map, 提取对象中的某一元素，例子中我取的是每个人的name，注意list中类型对应，如果取的是id或者班级，就应该是integer类型*/
        System.out.println("提取前：" + list);
        List<String> mapList = list.stream().map(Student::getName).collect(Collectors.toList());
        System.out.println("提取后：" + mapList);
    }

    @Test
    public void test7() {
        /*统计，统计所有人分数的和, 主要我设置的分数属性是double类型的，所以用mapToDouble，如果是int类型的，则需要用mapToInt*/
        double sum = list.stream().mapToDouble(Student::getScore).sum();
        System.out.println(sum);
        int count = list.stream().mapToInt(Student::getId).sum();
        System.out.println(count);
    }

    @Test
    public void test8() {
        /*分组， 按照字段中某个属性将list分组*/
        Map<Integer, List<Student>> map = list.stream().collect(Collectors.groupingBy(Student::getGrade));
        System.out.println("按年级分组" + map);
        /*然后再对map处理，这样就方便取出自己要的数据*/
        for (Map.Entry<Integer, List<Student>> entry : map.entrySet()) {
            System.out.println("key:" + entry.getKey());
            System.out.println("value:" + entry.getValue());
        }
    }

    @Test
    public void test9() {
        /*多重分组，先按年级分组，再按班级分组*/
        Map<Integer/*年级id*/, Map<Integer/*班级id*/, List<Student>>> groupMap = list.stream().collect(Collectors.groupingBy(Student::getGrade, Collectors.groupingBy(Student::getClasses)));
        System.out.println("按照年级再按班级分组：" + groupMap);
        System.out.println("取出一年级一班的list：" + groupMap.get(1).get(1));
    }

    @Test
    public void test10() {
        /*多重分组，一般多重分组后都是为了统计，比如说统计每个年级，每个班的总分数*/
        Map<Integer/*年级id*/, Map<Integer/*班级id*/, Double>> sumMap = list.stream().collect(Collectors.groupingBy(Student::getGrade, Collectors.groupingBy(Student::getClasses, Collectors.summingDouble(Student::getScore))));
        Map<Integer, Map<String, Double>> ms = list.stream().collect(Collectors.groupingBy(Student::getGrade, Collectors.toMap(Student::getName, Student::getScore, (e1, e2) -> e2)));
        System.out.println(sumMap);
        System.out.println("取出一年级一班的总分：" + sumMap.get(1).get(1));

        List<Double> ls = ms.values().stream().flatMap(r -> r.values().stream()).collect(Collectors.toList());
        System.out.println(ls);
    }

    @Test
    public void test11() {
        /*stream是链式的，这些功能是可以一起使用的，例如：计算每个年级每个班的及格人数*/
        Map<Integer/*年级*/, Map<Integer/*班级*/, Long/*人数*/>> integerMap = list.stream().filter(t -> t.getScore() >= 60).collect(Collectors.groupingBy(Student::getGrade, Collectors.groupingBy(Student::getClasses, Collectors.counting())));
        System.out.println("取出一年级一班及格人数：" + integerMap.get(1).get(1));
    }

    @Test
    public void test12() {
        // 按照默认顺序排序
        List<Student> ascList1 = list.stream().sorted().collect(Collectors.toList());
        System.out.println(ascList1);
    }

    @Test
    public void test13() {
        // 按照自然序排序(其实就是默认顺序)
        List<Student> ascList2 = list.stream().sorted(Comparator.naturalOrder()).collect(Collectors.toList());
        System.out.println(ascList2);
    }

    @Test
    public void test14() {
        // 按照默认顺序的相反顺序排序
        List<Student> descList = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
        System.out.println(descList);
    }

    @Test
    public void test15() {
        // 使用lambda表达式创建Function对象
        Function<Student, Integer> extractIdWay1 = Student::getId;

        // 使用方法引用简化lambda
        Function<Student, Integer> extractIdWay2 = Student::getId;

        // Comparator.comparing(Function keyExtractor)
        Comparator<Student> byId = Comparator.comparing(extractIdWay2);

        // 升序
        List<Student> ascList = list.stream().sorted(byId).collect(Collectors.toList());
        System.out.println(ascList);

        // 降序
        List<Student> descList = list.stream().sorted(byId.reversed()).collect(Collectors.toList());
        System.out.println(descList);

        Comparator<Student> nullNotAllowed = Comparator.comparing(Student::getId);
        Comparator<Student> allowNullComparator = Comparator.nullsFirst(nullNotAllowed);

        // 正常排序
        List<Student> result1 = list.stream().sorted(allowNullComparator).collect(Collectors.toList());
        System.out.println(result1);

        // 抛异常
        List<Student> result2 = list.stream().sorted(nullNotAllowed).collect(Collectors.toList());
        System.out.println(result2);

        // id升序
        Comparator<Student> byIdASC = Comparator.comparing(Student::getId);

        // named不分区大小写降序
        Comparator<Student> byNameDESC = Comparator.comparing(Student::getName, String.CASE_INSENSITIVE_ORDER).reversed();

        // 联合排序
        Comparator<Student> finalComparator = byIdASC.thenComparing(byNameDESC);

        List<Student> result = list.stream().sorted(finalComparator).collect(Collectors.toList());
        List<Student> result3 = list.stream().sorted(byNameDESC).collect(Collectors.toList());
        System.out.println(result);

//        Comparator<Student> byAge1 = Comparator.comparing(Student::getName);
        Comparator<Student> byAge1 = Comparator.comparing(r -> r.getName());
        Comparator<Student> byAge2 = Comparator.comparing(Student::getScore);
        List<Student> sortedAgeList1 = list.stream().sorted(byAge1).collect(Collectors.toList());
        List<Student> sortedAgeList2 = list.stream().sorted(byAge2).collect(Collectors.toList());
        System.out.println(sortedAgeList1);
        System.out.println(sortedAgeList2);

//        list.stream().reduce()
    }

    @Test
    public void test16() {
        // reduce 的用例
        // 字符串连接，concat = "ABCD"
        String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
        System.out.println(concat);
        // 求最小值，minValue = -3.0
        double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
        System.out.println(minValue);
        // 求和，sumValue = 10, 有起始值
        int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
        System.out.println(sumValue);
        // 求和，sumValue = 10, 无起始值
        sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
        System.out.println(sumValue);
        // 过滤，字符串连接，concat = "ace"
        concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
        System.out.println(concat);
        //上面代码例如第一个示例的 reduce()，第一个参数（空白字符）即为起始值，第二个参数（String::concat）为 BinaryOperator。
        // 这类有起始值的 reduce() 都返回具体的对象。而对于第四个示例没有起始值的 reduce()，由于可能没有足够的元素，返回的是 Optional，请留意这个区别。
    }

    @Test
    public void test17() {
//        System.out.println(list);
        Student[] students = list.stream().filter(r -> r.getScore() > 80).toArray(Student[]::new);
//        Student[] students = list.stream().filter(r->r.getScore()>80).toArray(r->{
//            System.out.println(r);
//            return new Student[r];
//        });
        System.out.println(Arrays.toString(students));
    }

    @Test
    public void test18() {
        list.stream().map(Student::getScore).map(Student::new).forEach(System.out::println);
    }


}